package com.kksoft.android.xiaoliaobaike.fetcher;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import net.tsz.afinal.FinalDb;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import com.kksoft.android.xiaoliaobaike.Const;
import com.kksoft.android.xiaoliaobaike.fetcher.thread.FetchArticlePageRangeThread;
import com.kksoft.android.xiaoliaobaike.fetcher.thread.FetchArticleThread;
import com.kksoft.android.xiaoliaobaike.fetcher.thread.FetchSeriesPageRangeThread;
import com.kksoft.android.xiaoliaobaike.fetcher.thread.FetchSeriesThread;
import com.kksoft.android.xiaoliaobaike.model.Article;
import com.kksoft.android.xiaoliaobaike.model.FetchTask;
import com.kksoft.android.xiaoliaobaike.model.Series;

@SuppressLint("HandlerLeak")
public class FetchTaskService extends Service {

	private static final String TAG = "FetchTaskService";

	public static final String CMD_START_OR_RESTORE = "CMD_START_OR_RESTORE";
	public static final String CMD_START = "CMD_START";
	public static final String CMD_RESTORE = "CMD_RESTORE";

	private int runningTask = 0;
	private long startTime = 0;

	private void runTask(FetchTask task) {
		runningTask++;
		if (task.getType() == FetchTask.TYPE_SERIES) {
			Log.d(TAG, "[SERIES]add task[" + task.getId() + "] to queue");
			execute(new FetchSeriesThread(seriesHandler, task));
		} else {
			Log.d(TAG, "[ARTICLE]add task[" + task.getId() + "] to queue");
			execute(new FetchArticleThread(articleHandler, task));
		}
	}

	private void saveTask(FetchTask task) {
		Integer insertId = db.save(task);
		if (insertId == null) {
			Log.e(TAG, "insert task error!");
		} else {
			task.setId(insertId);

			runTask(task);
		}
	}

	private void updateTask(FetchTask task) {
		db.update(task);
	}

	private FetchTask findTask(Integer id) {
		return db.findById(id, FetchTask.class);
	}

	public void startFetchAll() {
		if (runningTask > 0) {
			return;
		}
		if (db.count(FetchTask.class, null) > 0) {
			db.backupTable(FetchTask.class);
		}
		// db.deleteByWhere(FetchTask.class, null);
		fetchSeries();
	}

	public void startOrRestore() {
		if (runningTask > 0) {
			return;
		}

		startTime = System.currentTimeMillis();
		if (unfinishTaskCount() > 0) {
			restoreThread();
		} else {
			startFetchAll();
		}
	}

	public int unfinishTaskCount() {
		return db.count(FetchTask.class, "state<>" + FetchTask.STATE_FINISH
				+ " and state<>" + FetchTask.STATE_ERROR);
	}

	// 恢复线程
	public void restoreThread() {
		if (runningTask > 0) {
			return;
		}

		List<FetchTask> tasks = db.findAllByWhere(FetchTask.class, " state<>"
				+ FetchTask.STATE_FINISH + " and state<>"
				+ FetchTask.STATE_ERROR);

		if (tasks != null && tasks.size() > 0) {
			Log.d(TAG, "restoreThread size:" + tasks.size());
			for (FetchTask t : tasks) {
				runTask(t);
			}
		}
	}

	private void fetchSeries() {
		// 获得series页码范围
		Handler pageHandler = new Handler() {
			public void handleMessage(Message msg) {
				switch (msg.what) {
				case Const.MSG_SERIES_PAGE_RANGE:
					int pageStart = msg.arg1;
					int pageEnd = msg.arg2;
					if ((pageStart == 0 || pageEnd == 0) || pageStart > pageEnd) {
						Log.d(TAG, "no series found!");
					} else {
						for (int page = pageStart; page <= pageEnd; page++) {
							fetchSeriesPage(page);
						}
					}
					break;
				}
			}

		};
		// 获取所有页码
		execute(new FetchSeriesPageRangeThread(pageHandler));
	}

	private void execute(Runnable runnable) {
		executor.execute(runnable);
	}

	private void fetchSeriesPage(int page) {
		String seriesPageUrl = Const.getSeriesListUrl(page);
		FetchTask seriesTask = new FetchTask(null, FetchTask.TYPE_SERIES, null,
				null, "抓取series,page:" + page, seriesPageUrl, page,
				FetchTask.STATE_IDLE, null, 0, 0);

		saveTask(seriesTask);
	}

	private void fetchArticle(int seriesId) {
		// 获得article页码范围
		Handler pageHandler = new Handler() {

			public void handleMessage(Message msg) {
				switch (msg.what) {
				case Const.MSG_ARTICLE_PAGE_RANGE:
					int pageStart = msg.arg1;
					int pageEnd = msg.arg2;
					int seriesId = Integer.valueOf(msg.obj.toString());
					if ((pageStart == 0 || pageEnd == 0) || pageStart > pageEnd) {
						Log.d(TAG, "no articles found!");
					} else {
						for (int page = pageStart; page <= pageEnd; page++) {
							fetchArticlePage(seriesId, page);
						}
					}
					break;
				}
			}

		};
		// 获取所有页码
		execute(new FetchArticlePageRangeThread(pageHandler, seriesId));
	}

	private void fetchArticlePage(int seriesId, int page) {
		String articlePageUrl = Const.getArticleListUrl(seriesId, page);
		FetchTask articleTask = new FetchTask(null, FetchTask.TYPE_ARTICLE,
				seriesId, null, "抓取article,seriesId:" + seriesId + ",page:"
						+ page, articlePageUrl, page, FetchTask.STATE_IDLE,
				null, 0, 0);

		saveTask(articleTask);
	}

	FinalDb db;
	ExecutorService executor;
	SeriesHandler seriesHandler;
	ArticleHandler articleHandler;

	@Override
	public void onCreate() {
		super.onCreate();
		init();
	}

	private void init() {
		// 初始化线程池
		int cpuNums = Runtime.getRuntime().availableProcessors();
		// XXX
		executor = Executors.newFixedThreadPool(cpuNums * Const.POOL_SIZE);
		Log.d(TAG, "initial Executor Service size : "
				+ (cpuNums * Const.POOL_SIZE));

		db = FinalDb.create(getApplicationContext(), Const.DB_NAME, true);

		seriesHandler = new SeriesHandler();
		articleHandler = new ArticleHandler();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		CharSequence cmd = intent.getCharSequenceExtra("cmd");

		Log.d(TAG, "" + cmd);

		if (CMD_START_OR_RESTORE.equals(cmd)) {
			startOrRestore();
		} else if (CMD_START.equals(cmd)) {
			startFetchAll();
		} else if (CMD_RESTORE.equals(cmd)) {
			restoreThread();
		}

		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public boolean onUnbind(Intent intent) {
		return super.onUnbind(intent);
	}

	@Override
	public void onDestroy() {
		executor.shutdownNow();
		super.onDestroy();
	}

	class SeriesHandler extends Handler {
		public static final String TAG = "SeriesHandler";

		public void handleMessage(Message msg) {
			int taskId = msg.arg1;
			//int page = msg.arg2;

			final FetchTask task = findTask(taskId);

			boolean runOver = false;

			switch (msg.what) {
			case Const.MSG_URL_NOT_FOUND:
				runOver = true;
				Log.d(SeriesHandler.TAG,
						"MSG_URL_NOT_FOUND:" + msg.obj.toString());
				task.setState(FetchTask.STATE_ERROR);
				task.setMsg(msg.obj.toString());
				break;
			case Const.MSG_SERIES_FETCH_START:
				Log.d(SeriesHandler.TAG, "MSG_SERIES_FETCH_START task id:"
						+ msg.arg1 + ",page:" + msg.arg2);
				task.setStartTime(System.currentTimeMillis());
				task.setState(FetchTask.STATE_START);
				break;
			case Const.MSG_SERIES_FETCH_ERROR:
				runOver = true;
				Log.d(SeriesHandler.TAG, "MSG_SERIES_FETCH_ERROR task id:"
						+ msg.arg1 + ",page:" + msg.arg2 + ",err:" + msg.obj);
				task.setState(FetchTask.STATE_ERROR);
				task.setMsg(msg.obj.toString());
				break;
			case Const.MSG_SERIES_FETCH_FINISH:
				Log.d(SeriesHandler.TAG, "MSG_SERIES_FETCH_FINISH task id:"
						+ msg.arg1 + ",page:" + msg.arg2);
				break;
			case Const.MSG_SERIES_ANALYSIS_START:
				Log.d(SeriesHandler.TAG, "MSG_SERIES_ANALYSIS_START task id:"
						+ msg.arg1 + ",page:" + msg.arg2);
				task.setState(FetchTask.STATE_ANALYSIS);
				break;
			case Const.MSG_SERIES_ANALYSIS_ERROR:
				runOver = true;
				Log.d(SeriesHandler.TAG, "MSG_SERIES_ANALYSIS_ERROR task id:"
						+ msg.arg1 + ",page:" + msg.arg2 + ",err:" + msg.obj);
				task.setState(FetchTask.STATE_ERROR);
				task.setMsg(msg.obj.toString());
				break;
			case Const.MSG_SERIES_ANALYSIS_FINISH:
				runOver = true;
				Log.d(SeriesHandler.TAG, "MSG_SERIES_ANALYSIS_FINISH task id:"
						+ msg.arg1 + ",page:" + msg.arg2);
				task.setState(FetchTask.STATE_FINISH);
				break;
			case Const.MSG_SERIES_PAGE_RANGE:
				// 无用
				runOver = true;
				Log.d(SeriesHandler.TAG, "MSG_SERIES_PAGE_RANGE series id:"
						+ msg.obj + " page:" + msg.arg1 + "," + msg.arg2);
				break;
			case Const.MSG_SERIES_LIST:
				runOver = true;

				task.setState(FetchTask.STATE_FINISH);

				List<Series> seriesList = (List<Series>) msg.obj;

				Log.d(SeriesHandler.TAG, "MSG_SERIES_LIST task id:" + msg.arg1
						+ ",page:" + msg.arg2 + ", size" + seriesList.size());

				StringBuffer idBuffer = new StringBuffer();

				if (seriesList.size() > 0) {

					for (Series s : seriesList) {
						fetchArticle(s.getSeriesId());

						List<Series> inDb = db.findAllByWhere(Series.class,
								"seriesId=" + s.getSeriesId());

						String id = null; // i: 插入，u: 更新
						Integer seriesId = null;
						if (inDb == null || inDb.size() == 0
								|| !inDb.contains(s)) {
							seriesId = db.save(s);
							id = "i:" + seriesId;
						} else {
							Series dbs = inDb.get(inDb.indexOf(s));
							seriesId = dbs.getId();
							id = "u:" + seriesId;
							s.setId(dbs.getId());
							db.update(s);
						}

						idBuffer.append(id + ",");
					}

				}

				if (idBuffer.length() > 0) {
					idBuffer.deleteCharAt(idBuffer.length() - 1);
				}

				task.setMsg("series ids:[" + idBuffer.toString() + "]");

				break;
			}

			if (runOver) {
				task.setFinishTime(System.currentTimeMillis());
				runningTask--;
			}

			updateTask(task);
		}
	}

	// TODO
	@SuppressLint("HandlerLeak")
	class ArticleHandler extends Handler {
		public static final String TAG = "ArticleHandler";

		public void handleMessage(Message msg) {
			int taskId = msg.arg1;
			//int page = msg.arg2;

			FetchTask task = findTask(taskId);

			boolean runOver = false;

			switch (msg.what) {
			case Const.MSG_URL_NOT_FOUND:
				runOver = true;
				Log.d(ArticleHandler.TAG,
						"MSG_URL_NOT_FOUND:" + msg.obj.toString());
				task.setState(FetchTask.STATE_ERROR);
				task.setMsg(msg.obj.toString());
				break;
			case Const.MSG_ARTICLE_FETCH_START:
				Log.d(ArticleHandler.TAG, "MSG_ARTICLE_FETCH_START task id:"
						+ msg.arg1 + ",page:" + msg.arg2);
				task.setStartTime(System.currentTimeMillis());
				task.setState(FetchTask.STATE_START);
				break;
			case Const.MSG_ARTICLE_FETCH_ERROR:
				runOver = true;
				Log.d(ArticleHandler.TAG, "MSG_ARTICLE_FETCH_ERROR task id:"
						+ msg.arg1 + ",page:" + msg.arg2 + ",err:" + msg.obj);
				task.setState(FetchTask.STATE_ERROR);
				task.setMsg(msg.obj.toString());
				break;
			case Const.MSG_ARTICLE_FETCH_FINISH:
				Log.d(ArticleHandler.TAG, "MSG_ARTICLE_FETCH_FINISH task id:"
						+ msg.arg1 + ",page:" + msg.arg2);
				break;
			case Const.MSG_ARTICLE_ANALYSIS_START:
				Log.d(ArticleHandler.TAG, "MSG_ARTICLE_ANALYSIS_START task id:"
						+ msg.arg1 + ",page:" + msg.arg2);
				task.setState(FetchTask.STATE_ANALYSIS);
				break;
			case Const.MSG_ARTICLE_ANALYSIS_ERROR:
				runOver = true;
				Log.d(ArticleHandler.TAG, "MSG_ARTICLE_ANALYSIS_ERROR task id:"
						+ msg.arg1 + ",page:" + msg.arg2 + ",err:" + msg.obj);
				task.setState(FetchTask.STATE_ERROR);
				task.setMsg(msg.obj.toString());
				break;
			case Const.MSG_ARTICLE_ANALYSIS_FINISH:
				runOver = true;
				Log.d(ArticleHandler.TAG,
						"MSG_ARTICLE_ANALYSIS_FINISH task id:" + msg.arg1
								+ ",page:" + msg.arg2);
				task.setState(FetchTask.STATE_FINISH);
				break;
			case Const.MSG_ARTICLE_PAGE_RANGE:
				// 无用
				runOver = true;
				Log.d(ArticleHandler.TAG, "MSG_ARTICLE_PAGE_RANGE series id:"
						+ msg.obj + " page:" + msg.arg1 + "," + msg.arg2);
				break;
			case Const.MSG_ARTICLE_LIST:
				runOver = true;

				task.setState(FetchTask.STATE_FINISH);

				List<Article> articleList = (List<Article>) msg.obj;

				Log.d(ArticleHandler.TAG, "MSG_ARTICLE_LIST task id:"
						+ msg.arg1 + ",page:" + msg.arg2 + ", size"
						+ articleList.size());

				StringBuffer idBuffer = new StringBuffer();

				if (articleList.size() > 0) {
					for (Article a : articleList) {
						List<Article> inDb = db.findAllByWhere(Article.class,
								"articleId=" + a.getArticleId());

						String id = null; // i: 插入，u: 更新
						Integer articleId = null;
						if (inDb == null || inDb.size() == 0
								|| !inDb.contains(a)) {
							articleId = db.save(a);
							id = "i:" + articleId;
						} else {
							Article dbs = inDb.get(inDb.indexOf(a));
							articleId = dbs.getId();
							id = "u:" + articleId;
							a.setId(dbs.getId());
							db.update(a);
						}

						idBuffer.append(id + ",");
					}
				}

				if (idBuffer.length() > 0) {
					idBuffer.deleteCharAt(idBuffer.length() - 1);
				}

				task.setMsg("article ids:[" + idBuffer.toString() + "]");

				break;
			}

			if (runOver) {
				task.setFinishTime(System.currentTimeMillis());
				reduceTask();
			}
			updateTask(task);
		}
	}

	public void reduceTask() {
		if (runningTask == 0)
			return;

		runningTask--;


		if (runningTask <= 0) {
			long finishTime = System.currentTimeMillis();
			
			Intent intent = new Intent(Const.BROADCAST_MSG_FETCH_START);
			intent.putExtra(Const.FETCH_START_TIME, startTime);
			intent.putExtra(Const.FETCH_FINISH_TIME, finishTime);
			sendBroadcast(intent);
			runningTask = 0;
		}
	}

}